docs: correct ARC-403 mint semantics and disclose known trust boundaries - #24
Merged
Conversation
alejoamiras
force-pushed
the
docs/audit-followups
branch
from
August 18, 2026 15:20
2470028 to
8496fd5
Compare
Follow-ups from a security audit of the Noir contracts. Documentation only — no contract logic changes. - token/main.nr: the two ARC-403 hook wrappers documented `from` as "(zero address for mints)", describing the spec's parameter semantics rather than this contract's behaviour: no mint path calls either hook. The comment was the single strongest reason two independent reviews mistook the deliberate design for an authorization bypass. Replaced with a pointer to the README. - token/README: state the consequence of mints being unhooked — an authorization contract is not a universal kill switch, so a compromised minter cannot be contained by pausing the hook. Notes the open ARC-403 question (hooking mints would enable pause and supply caps but still not recipient screening, since `to` is not forwarded). - multitoken/README: same kill-switch caveat, plus a new "Commitment trust model" section. `initialize_transfer_commitment` binds the recipient and completer but deliberately not `token_id`/`amount`, which the completer supplies later. That is documented intent and is safe when the completer is already trusted for the value, but it makes a commitment unsuitable as a payment guarantee in escrow or handoff patterns — a wider surface than Token, where only the amount is free. - vault/README + root README: the existing warning disclosed the conversion overflow and privacy limits but not the reentrancy exposure. The vault's protection is the order of its operations, which assumes a token transfer is indivisible; when the asset token has an ARC-403 hook, that hook runs mid-transfer and can observe the vault in the intermediate state the ordering exists to prevent. Advise wrapping only assets with no hook, or a fully trusted one. Validated: aztec-nargo fmt --check clean, aztec compile OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alejoamiras
force-pushed
the
docs/audit-followups
branch
from
August 18, 2026 15:43
8496fd5 to
d24dacd
Compare
Benchmark Comparison
Contract: escrow
Contract: logic
Contract: multitoken
Contract: nft
Contract: token
Contract: vault
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IlyasRidhuan
approved these changes
Aug 19, 2026
alejoamiras
added a commit
that referenced
this pull request
Aug 19, 2026
The conversions compute `a * b / denominator`. Done natively in u128 the intermediate product overflows for large-but-legitimate inputs; Noir range-checks u128, so the transaction reverts rather than wrapping. That is an availability bug (audit F-004): a vault whose totals reach the range can no longer be deposited to or withdrawn from, permanently locking every participant's funds. Both sites carried TODOs. New `conversion.nr` module with a `mul_div` primitive that widens both operands to noir-bignum's U512 (the same library the escrow's key derivation already uses), multiplies and divides there, then narrows the quotient back to u128, asserting it fits. u128 * u128 < 2^256 and the U512 modulus is 2^512, so the product is exact — no modular wraparound. Rounding is unchanged: both old and new return floor(p/d) + (round_up && p%d != 0). For every input the old code accepted the results are identical; the widening only extends the domain that succeeds. This matters because the vault's economic safety depends on rounding always favouring the vault — a shift in either direction would leak value between the vault and its depositors. `mul_div` also rejects a zero denominator explicitly: noir-bignum's constrained udiv_mod fails on it, but its unconstrained path assumes non-zero and would return a meaningless witness. Unreachable from the vault (denominators are total_assets+1 and total_supply+vault_offset with vault_offset >= 1) but the helper is now safe in isolation. Validated: vault_contract 195 Noir tests (188 pre-existing all still green — the strongest evidence rounding did not shift — plus 7 new covering limb round-trips, rounding direction both ways, products that previously overflowed, max operands, and the two revert guards). aztec compile OK. Codex adversarial review: correct, rounding invariance proven algebraically, no value-leak path; its zero-denominator hardening is applied. Note: the vault README still describes the overflow as a known issue. That warning block is rewritten in PR #24 (unmerged); leaving it there avoids a three-way conflict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Documentation follow-ups from a security audit of the Noir contracts. No contract logic changes — comments and READMEs only.
1.
token/main.nr— remove a comment that contradicts the codeBoth ARC-403 hook wrappers documented
fromas "(zero address for mints)". No mint path calls either hook, so the parenthetical described the spec's parameter semantics rather than this contract's behaviour.It was load-bearing in the wrong direction: it was the strongest single reason two independent reviews classified the deliberate design as an authorization bypass (CWE-862). Replaced with a pointer to the README.
2.
token/README— state what unhooked mints implyThe README already documented that mints are unhooked and why. Added the consequence for integrators: an authorization contract is not a universal kill switch. Pausing it stops transfers and burns while the
mintercan still issue supply — so a compromised minter key cannot be contained by the hook,minterbeing a single immutable address where the hook can encode multisig/timelock/pause.Also records the open ARC-403 question: hooking mints (with
from= zero) would enable pause and supply-cap policies but still not recipient screening, sincetois deliberately not forwarded. That's a spec decision for the forum draft, not a patch — flagging it here rather than changing behaviour unilaterally.3.
multitoken/README— new "Commitment trust model" sectioninitialize_transfer_commitmentbinds the recipient, randomness and completer, but deliberately nottoken_idoramount— the completer supplies both at completion. That is documented intent and is genuinely useful when the value depends on public state.The gap was that nothing said which patterns it is unsafe for. A commitment is not a payment guarantee: a malicious completer can satisfy it with a negligible amount of an arbitrary id, including one they created. Safe when the completer is already trusted for the value; unsafe for escrow, marketplace and handoff flows. Noted that this is a wider surface than
Token, where the token is implicit and only the amount is completer-chosen.Same kill-switch caveat as (2) added, since MultiToken carries the identical mints-unhooked design.
4.
vault/README+ rootREADME— disclose the reentrancy exposureThe existing warning disclosed the conversion overflow and the privacy limitations, but not the reentrancy exposure, which understated the risk profile.
The vault's protection against reentrancy is the order of its operations (assets in before shares minted, shares burned before assets paid out), which assumes a token transfer is indivisible. It isn't: when the asset token has an ARC-403
auth_contract, that contract runs during the transfer, before balances move, and can observe the vault in exactly the intermediate state the ordering exists to exclude. Reading the share price there yields a value no completed operation produces.The warning now advises wrapping only assets whose
get_auth_contract()is zero, or one whose authorization contract is fully trusted. Vaults over unhooked tokens are unaffected.Validation
aztec-nargo fmt --checkclean ·aztec compileOK. No test changes (nothing behavioural changed).Note
The
.nrformatting fix for main's failingFormatcheck lives in #23 and is deliberately not duplicated here.🤖 Generated with Claude Code